package com.danco.training.zaneuskaya.service.impl;

import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;

import com.danco.training.zaneuskaya.api.OrderService;
import com.danco.training.zaneuskaya.cswworker.CSVReader;
import com.danco.training.zaneuskaya.cswworker.CSVSaver;
import com.danco.training.zaneuskaya.cswworker.Separator;
import com.danco.training.zaneuskaya.domain.Book;
import com.danco.training.zaneuskaya.domain.Order;
import com.danco.training.zaneuskaya.service.comparator.OrderComparatorByOrderDate;
import com.danco.training.zaneuskaya.service.comparator.OrderComparatorByPrice;
import com.danco.training.zaneuskaya.service.comparator.OrderComparatorByState;
import com.danco.training.zaneuskaya.storage.BookStorage;
import com.danco.training.zaneuskaya.storage.OrderStorage;


public class OrderServiceImpl implements Serializable, OrderService {

	/**
	 * 
	 */
	private static final long serialVersionUID = -1305548171488123635L;
	private OrderStorage orderStorage;
	private BookStorage bookStorage;
	private BookServiceImpl bookServiceImpl = BookServiceImpl
			.getBookStorageSeviceImplInstance();
	private static final Logger logger = Logger
			.getLogger(OrderServiceImpl.class);

	private static OrderServiceImpl orderStorageServiceInstance;

	private OrderComparatorByOrderDate orderComparatorByOrderDate = new OrderComparatorByOrderDate();
	private OrderComparatorByPrice orderComparatorByPrice = new OrderComparatorByPrice();
	private OrderComparatorByState orderComparatorByState = new OrderComparatorByState();
	
	

	public OrderServiceImpl() {

		this.orderStorage = OrderStorage.getOrderStorageInstance();
		this.bookStorage = BookStorage.getBookStorageInstance();
	}

	public List<Order> getList() {
		List<Order> res = new ArrayList<Order>();
		res = orderStorage.getOrders();
		logger.trace("Order list");
		return res;
	}

	public List<Integer> getListId() {
		List<Integer> res = new ArrayList<Integer>();
		for (Order or : orderStorage.getOrders()) {
			res.add(or.getId());
		}

		return res;

	}

	public int generateUnexistedId() {
		int i = 1;
		while (getListId().contains(i)) {
			i++;
		}
		return i;
	}

	public List<Order> getExecutedOrders(List<Order> orders) {
		List<Order> res = new ArrayList<Order>();
		for (Order order : orders) {
			if (order.isExecuted()) {
				res.add(order);

			}

		}
		logger.trace("Excecuted orders  are founded");
		return res;
	}

	public List<Order> getNotExecutedOrders(List<Order> orders) {
		List<Order> res = new ArrayList<Order>();
		for (Order order : orders) {
			if (!order.isExecuted()) {
				res.add(order);

			}

		}
		logger.trace("Not excecuted orders  are founded");
		return res;
	}

	public List<Order> getExecutedOrdersForPeriod(List<Order> orders,
			Date from, Date to) {
		List<Order> res = new ArrayList<Order>();
		for (Order order : getExecutedOrders(orders)) {

			if (order.getOrderDate().after(from)
					&& order.getOrderDate().before(to)) {
				res.add(order);

			}
		}
		logger.trace("Excecuted orders for period are founded");
		return res;
	}

	public List<Order> sortOrdersByOrderDate(List<Order> orders) {
		orders.sort(orderComparatorByOrderDate);
		logger.trace("Orders are sorted by execution date");
		return orders;
	}

	public List<Order> sortOrdersByTotalPrice(List<Order> orders) {
		orders.sort(orderComparatorByPrice);
		logger.trace("Orders are sorted by total price");
		return orders;
	}

	public List<Order> sortOrdersByOrderState(List<Order> orders) {
		orders.sort(orderComparatorByState);
		logger.trace("Orders are sorted by state");
		return orders;

	}

	public List<Order> sortExecutedOrdersByOrderDate(List<Order> orders,
			Date from, Date to) {
		List<Order> res = new ArrayList<Order>();
		res = getExecutedOrdersForPeriod(orders, from, to);
		res.sort(orderComparatorByOrderDate);
		logger.trace("Executed orders are sorted by execution date");

		return res;
	}

	public List<Order> sortExecutedOrdersByPrice(List<Order> orders, Date from,
			Date to) {
		List<Order> res = new ArrayList<Order>();
		res = getExecutedOrdersForPeriod(orders, from, to);
		res.sort(orderComparatorByPrice);
		logger.trace("Executed orders are sorted by price");
		return res;
	}

	public Order getById(Integer id) {
		for (Order order : orderStorage.getOrders()) {
			if (order.getId() == id) {
				logger.trace("Order is founded in Orderstorage by id");
				return order;
			}

		}
		logger.info("Order not founded in Orderstorage ");
		return null;
	}

	public boolean addBookToOrder(Order order, Book book) {

		try {
			if (order.getBooks().contains(book)) {
				for (Book b : order.getBooks()) {
					if (b.getId() == book.getId()) {
						b.setQuantity((b.getQuantity()) + 1);
						logger.trace("Instance of the existing book is added in Order");
					}

				}
			} else {
				book.setQuantity(1);
				order.getBooks().add(book);
				logger.trace("Instance of the new book is added in Order");
			}

			order.setTotalPrice(countPrice(order));
			addEntity(order);
			return true;
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			return false;
		}
	}

	public boolean removeBookFromOrder(Order order, Book book) {
		try {
			if (order.getBooks().contains(book)) {
				if (order.getBooks().remove(book)) {
					order.setTotalPrice(countPrice(order));
					logger.trace("Instance of book is removed from Order");

				}
			}
			return true;
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			return false;
		}

	}

	public double countPrice(Order order) {
		double sum = 0;
		for (Book book : order.getBooks()) {
			sum += book.getPrice() * book.getQuantity();
		}
		return sum;
	}

	public Order createOrderCopy(Order order) throws CloneNotSupportedException {
		Order copy;

		copy = order.clone();
		copy.setBooks(order.getBooks());
		copy.setExecuted(order.isExecuted());
		copy.setTotalPrice(order.getTotalPrice());
		copy.setOrderDate(order.getOrderDate());
		copy.setId(generateUnexistedId());
		addEntity(copy);
		return copy;
	}

	public boolean addEntity(Order order) {

		return orderStorage.getOrders().add(order);

	}

	public boolean executeOrderById(Integer id) {

		getById(id).setExecuted(true);
		getById(id).setOrderDate(new Date());

		for (Book b : bookStorage.getBooks()) {
			if (getById(id).getBooks().contains(b)) {
				b.setQuantity(b.getQuantity() - 1);
			} else {
				b.setAvailable(false);
			}
		}
		return true;

	}

	public boolean removeEntityById(Integer index) {

		System.out.println("Order is rejected");

		return true;
	}

	public void saveEntityListToFile(String file) {
		ArrayList<String> res = new ArrayList<String>();
		for (Order b : getList()) {
			res.add(entityDetails(b));
		}
		try {
			CSVSaver.saveListToFile(file, res);
		} catch (IOException e) {
			logger.error(e.getMessage(), e);
		}
	}

	public ArrayList<Order> readEntityList(String file) {
		ArrayList<Order> res = new ArrayList<Order>();
		for (int i = 0; i < CSVReader.readList(file).size(); i++) {
			String order[] = CSVReader.readFile(file)[i]
					.split(Separator.SEPARATOR.getS());
			Order or = new Order(Integer.parseInt(order[0]));
			addEntity(or);
			String books[] = order[1].split(Separator.BOOK_SEPARATOR_IN_ORDER
					.getS());
			for (int j = 0; j < books.length - 1; j++) {
				addBookToOrder(or,
						bookServiceImpl.getById(Integer.parseInt(books[j])));
			}
			or.setExecuted(Boolean.parseBoolean(order[2]));
			or.setTotalPrice(Double.parseDouble(order[3]));
			res.add(or);
		}
		return res;
	}

	public static OrderServiceImpl getOrderStorageServiceInstance() {
		if (orderStorageServiceInstance == null) {
			orderStorageServiceInstance = new OrderServiceImpl();
		}
		return orderStorageServiceInstance;
	}

	public String entityDetails(Order t) {

		StringBuffer str = new StringBuffer(t.getId()
				+ Separator.SEPARATOR.getS());
		for (Book book : t.getBooks()) {
			str.append(book.getId());
			str.append(Separator.BOOK_SEPARATOR_IN_ORDER.getS());

		}
		str.append(Separator.SEPARATOR.getS());
		str.append(t.isExecuted() + Separator.SEPARATOR.getS());
		str.append(t.getTotalPrice());
		return str.toString();

	}

	@Override
	public boolean updateEntity(Order e1, Order e2) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean removeEntity(Order e) {
		// TODO Auto-generated method stub
		return false;
	}

}
